package com.winbomb.simulife.impl;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import android.util.Log;

import com.winbomb.simulife.World;
import com.winbomb.simulife.base.IOperation;
import com.winbomb.simulife.base.IOperationManager;

public class OperationManager implements IOperationManager {

	private static final String TAG = "OperationManager";

	private static final String CONFIG_FILE_PATH = "/com/winbomb/simulife/impl/operations/config.xml";
	private static final String CLASS_NAME_PREFIX = "com.winbomb.simulife.impl.operations.";

	private static World world;
	private static OperationManager instance = null;
	private static IOperation[] allOperations;

	private OperationManager(World paramWorld) {
		world = paramWorld;

		SAXParserFactory factory = SAXParserFactory.newInstance();
		try {
			SAXParser parser = factory.newSAXParser();
			InputStream fis = EventManager.class.getResourceAsStream(CONFIG_FILE_PATH);

			XMLContentHandler handler = new XMLContentHandler();
			parser.parse(fis, handler);

			OperationElem[] optElems = handler.getOperations();

			allOperations = parseOperationElems(optElems);

		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	@SuppressWarnings("unchecked")
	private IOperation[] parseOperationElems(OperationElem[] optElems) {
		List<IOperation> optList = new ArrayList<IOperation>();
		for (OperationElem opt : optElems) {
			if (opt == null) {
				continue;
			}

			String classFullName = CLASS_NAME_PREFIX + opt.getClassName();
			Class<BaseOperation> clazz;
			try {
				clazz = (Class<BaseOperation>) Class.forName(classFullName);
				Constructor<BaseOperation> constructor = clazz.getConstructor(World.class);
				BaseOperation optImpl = (BaseOperation) constructor.newInstance(world);

				optImpl.optName = opt.optName;
				optImpl.optCost = opt.timeCost;
				optImpl.animName = opt.pictureName;
				optImpl.iconName = opt.iconName;

				optList.add(optImpl);
			} catch (ClassNotFoundException e) {
				Log.w(TAG, "Could not found class: " + classFullName);
			} catch (NoSuchMethodException e) {
				Log.w(TAG, "Cound not find proper constructor while loading class: "
						+ classFullName);
			} catch (Exception ex) {
				Log.w(TAG, "Error occured while loading class: " + classFullName + " caused by "
						+ ex);
			}

		}

		return optList.toArray(new IOperation[optList.size()]);
	}

	public synchronized static OperationManager getInstance(World paramWorld) {
		if (instance == null) {
			instance = new OperationManager(paramWorld);
		}

		return instance;
	}

	@Override
	public List<IOperation> getEnabledOperations() {

		List<IOperation> operations = new ArrayList<IOperation>();

		for (IOperation opt : allOperations) {
			if (opt.isEnable()) {
				operations.add(opt);
			}
		}
		return operations;
	}

	class XMLContentHandler extends DefaultHandler {

		private List<OperationElem> optList;
		private OperationElem opt;
		private String preTag;
		private static final String ELEM_OPERATION = "Operation";
		private static final String ELEM_CLASS_NAME = "ClassName";
		private static final String ELEM_PICTURE = "Picture";
		private static final String ELEM_TIME_COST = "TimeCost";
		private static final String ELEM_ICON_NAME = "Icon";
		private static final String ELEM_OPT_NAME = "Name";

		public OperationElem[] getOperations() {
			return optList.toArray(new OperationElem[optList.size()]);
		}

		/**
		 * 要开始读取xml文件的时候调用的方法 初始化persons
		 */
		@Override
		public void startDocument() throws SAXException {
			optList = new ArrayList<OperationElem>();
		}

		/**
		 * sax 读取到文本节点的时候调用了这个方法
		 */
		@Override
		public void characters(char[] ch, int start, int length) throws SAXException {

			if (preTag != null && opt != null) {
				String data = new String(ch, start, length);
				if (ELEM_CLASS_NAME.equals(preTag)) {
					opt.setClassName(data);
				} else if (ELEM_PICTURE.equals(preTag)) {
					opt.setPictureName(data);
				} else if (ELEM_TIME_COST.equals(preTag)) {
					int timeCost = 2;
					try {
						timeCost = Integer.parseInt(data);
					} catch (NumberFormatException ex) {
						Log.w(TAG, "Value of TimeCost invalid, Default value (2) will be set.");
					}
					opt.setTimeCost(timeCost);
				} else if (ELEM_ICON_NAME.equals(preTag)) {
					opt.setIconName(data);
				} else if (ELEM_OPT_NAME.equals(preTag)) {
					opt.setOptName(data);
				}
			}
		}

		/**
		 * sax 读取到元素节点的时候用到这个方法；
		 */
		@Override
		public void startElement(String uri, String localName, String qName, Attributes attributes)
				throws SAXException {
			if (ELEM_OPERATION.equals(localName)) {
				opt = new OperationElem();
			}
			preTag = localName;
		}

		/**
		 * 这个方法是每次遇到结束的标签都会执行的 并不是只遇到最后的结尾才调用
		 */

		@Override
		public void endElement(String uri, String localName, String name) throws SAXException {

			if (ELEM_OPERATION.equals(localName) && opt != null) {
				optList.add(opt);
				opt = null;
			}
			preTag = null;
		}

	}
}

class OperationElem {

	String className;
	String optName;
	String iconName;
	String pictureName;
	int timeCost;

	public String toString() {
		StringBuffer sb = new StringBuffer();

		sb.append("ClassName: ");
		sb.append(className);
		sb.append("   PictureName: ");
		sb.append(pictureName);
		sb.append("   TimeCost: ");
		sb.append(timeCost);

		return sb.toString();
	}

	public String getClassName() {
		return className;
	}

	public void setClassName(String className) {
		this.className = className;
	}

	public String getPictureName() {
		return pictureName;
	}

	public void setPictureName(String pictureName) {
		this.pictureName = pictureName;
	}

	public int getTimeCost() {
		return timeCost;
	}

	public void setTimeCost(int timeCost) {
		this.timeCost = timeCost;
	}

	public String getOptName() {
		return optName;
	}

	public void setOptName(String optName) {
		this.optName = optName;
	}

	public String getIconName() {
		return iconName;
	}

	public void setIconName(String iconName) {
		this.iconName = iconName;
	}
}